/**
 * CoursesComponent.java 21 juil. 07
 *
 * Copyright (c) 2007 windu.2b <jcaddie@windu2b.org>. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.windu2b.jcaddie.swing;

import java.awt.*;
import java.awt.geom.*;

import javax.swing.*;

import org.windu2b.jcaddie.model.*;

/**
 * @author windu.2b
 * 
 */
public class CoursesComponent extends JComponent
{
	/**
	 * 
	 */
	private static final long	serialVersionUID	= 6869242700914246662L;


	private static final float	MARGIN	         = 40;


	private Caddie	           caddie;


	private UserPreferences	   preferences;


	private float	           scale	         = 1f;


	private boolean	           selectionScrollUpdated;


	private Rectangle2D	       planBoundsCache;




	public CoursesComponent( Caddie caddie, UserPreferences preferences,
	        CoursesController controller )
	{
		this.caddie = caddie;
		this.preferences = preferences;
		setOpaque( true );
	}




	@Override
	public Dimension getPreferredSize()
	{
		if ( isPreferredSizeSet() )
		{
			return super.getPreferredSize();
		}
		else
		{
			Insets insets = getInsets();
			Rectangle2D coursesBounds = getCoursesBounds();

			return new Dimension( Math.round( ( ( float ) coursesBounds
			        .getWidth() + MARGIN * 2 )
			        * scale )
			        + insets.left + insets.right, Math
			        .round( ( ( float ) coursesBounds.getHeight() + MARGIN * 2 )
			                * scale )
			        + insets.top + insets.bottom );
		}
	}




	/**
	 * Returns the bounds of the plan displayed by this component.
	 */
	private Rectangle2D getCoursesBounds()
	{
		Rectangle2D coursesBounds = new Rectangle2D.Float( 0, 0, 1000, 1000 );
		// TODO inclure la gestion de la taille des produits dans la liste de
		// courses
		return coursesBounds;
	}




	@Override
	protected void paintComponent( Graphics g )
	{
		Graphics2D g2D = ( Graphics2D ) g.create();
		paintBackground( g2D );

		Insets insets = getInsets();
		g2D.clipRect( insets.left, insets.top, getWidth() - insets.left
		        - insets.right, getHeight() - insets.top - insets.bottom );

		Rectangle2D coursesBounds = getCoursesBounds();
		g2D.translate( insets.left + ( MARGIN - coursesBounds.getMinX() )
		        * scale, insets.top + ( MARGIN - coursesBounds.getMinY() )
		        * scale );
		g2D.scale( scale, scale );
		paintContent( g2D );
		g2D.dispose();
	}




	private void paintBackground( Graphics2D g2D )
	{
		if ( isOpaque() )
		{
			Color backgroundColor = UIManager.getColor( "window" );

			g2D.setColor( backgroundColor );
			g2D.fillRect( 0, 0, getWidth(), getHeight() );
		}
	}




	/**
	 * @param g2d
	 */
	private void paintContent( Graphics2D g2D )
	{
		g2D.setColor( getForeground() );

		// TODO dessiner ici les éléments des courses
	}




	/**
	 * Ensures selected items are visible at screen and moves scroll bars if
	 * needed.
	 */
	public void makeSelectionVisible()
	{
		// As multiple selections may happen during an action,
		// make the selection visible the latest possible to avoid multiple
		// changes
		if ( !this.selectionScrollUpdated )
		{
			this.selectionScrollUpdated = true;
			EventQueue.invokeLater( new Runnable()
			{
				public void run()
				{
					selectionScrollUpdated = false;
					Area area = new Area();
					boolean selectionContainsVisibleObjects = false;
					for ( Object item : caddie.getSelectedItems() )
					{
						if ( item instanceof CaddiePieceOfProduct )
						{
							CaddiePieceOfProduct piece = ( CaddiePieceOfProduct ) item;
							if ( piece.isVisible() )
							{
								/*
								 * area.add( new Area( getShape( ( piece )
								 * .getPoints() ) ) );
								 */
								selectionContainsVisibleObjects = true;
							}
						}
					}

					if ( selectionContainsVisibleObjects )
					{
						Rectangle pixelBounds = getShapePixelBounds( area );
						pixelBounds.grow( 5, 5 );
						scrollRectToVisible( pixelBounds );
					}
				}
			} );
		}
	}




	/**
	 * Returns the shape matching the coordinates in <code>points</code>
	 * array.
	 */
	private Shape getShape( float[][] points )
	{
		GeneralPath wallPath = new GeneralPath();
		wallPath.moveTo( points[0][0], points[0][1] );
		for ( int i = 1; i < points.length; i++ )
		{
			wallPath.lineTo( points[i][0], points[i][1] );
		}
		wallPath.closePath();
		return wallPath;
	}




	/**
	 * Returns the bounds of <code>shape</code> in pixels coordinates space.
	 */
	private Rectangle getShapePixelBounds( Shape shape )
	{
		Rectangle2D shapeBounds = shape.getBounds2D();
		return new Rectangle( convertXModelToPixel( ( float ) shapeBounds
		        .getMinX() ), convertYModelToPixel( ( float ) shapeBounds
		        .getMinY() ), ( int ) Math.round( shapeBounds.getWidth()
		        * this.scale ), ( int ) Math.round( shapeBounds.getHeight()
		        * this.scale ) );
	}




	/**
	 * Returns <code>x</code> converted in model coordinates space.
	 */
	public float convertXPixelToModel( int x )
	{
		Insets insets = getInsets();
		Rectangle2D planBounds = getCoursesBounds();
		return ( x - insets.left ) / this.scale - MARGIN
		        + ( float ) planBounds.getMinX();
	}




	/**
	 * Returns <code>y</code> converted in model coordinates space.
	 */
	public float convertYPixelToModel( int y )
	{
		Insets insets = getInsets();
		Rectangle2D planBounds = getCoursesBounds();
		return ( y - insets.top ) / this.scale - MARGIN
		        + ( float ) planBounds.getMinY();
	}




	/**
	 * Returns <code>x</code> converted in view coordinates space.
	 */
	private int convertXModelToPixel( float x )
	{
		Insets insets = getInsets();
		Rectangle2D planBounds = getCoursesBounds();
		return ( int ) Math.round( ( x - planBounds.getMinX() + MARGIN )
		        * this.scale )
		        + insets.left;
	}




	/**
	 * Returns <code>y</code> converted in view coordinates space.
	 */
	private int convertYModelToPixel( float y )
	{
		Insets insets = getInsets();
		Rectangle2D planBounds = getCoursesBounds();
		return ( int ) Math.round( ( y - planBounds.getMinY() + MARGIN )
		        * this.scale )
		        + insets.top;
	}
}